/*

  Copyright (C) 2000, 2001 Silicon Graphics, Inc.  All Rights Reserved.

  This program is free software; you can redistribute it and/or modify it
  under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.

  This program is distributed in the hope that it would be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

  Further, this software is distributed without any warranty that it is
  free of the rightful claim of any third person regarding infringement 
  or the like.  Any license provided herein, whether implied or 
  otherwise, applies only to this software file.  Patent licenses, if 
  any, provided herein do not apply to combinations of this program with 
  other software, or any other product whatsoever.  

  You should have received a copy of the GNU General Public License along
  with this program; if not, write the Free Software Foundation, Inc., 59
  Temple Place - Suite 330, Boston MA 02111-1307, USA.

  Contact information:  Silicon Graphics, Inc., 1600 Amphitheatre Pky,
  Mountain View, CA 94043, or:

  http://www.sgi.com

  For further information regarding this notice, see:

  http://oss.sgi.com/projects/GenInfo/NoticeExplan

*/


/* ====================================================================
 * ====================================================================
 *
 *
 * Revision history:
 *  13-Feb-95 - Original Version
 *
 * Description:
 *  Main driver for the entire backend.
 *
 * ====================================================================
 * ====================================================================
 */

#include <sys/types.h>
#include <elf.h>		    /* for wn.h */
#include <cmplrs/rcodes.h>
#include <dirent.h>
#include <libgen.h>

#include "defs.h"
#include "dso.h"		    /* for load_so() */
#include "errors.h"		    /* Set_Error_Tables(), etc. */
#include "err_host.tab"		    /* load all the error messages */
#include "erglob.h"		    /* for EC_ errmsg */
#include "erauxdesc.h"		    /* for BE error messages */
#include "mempool.h"		    /* for MEM_Initialze()  */
#include "phase.h"		    /* for PHASE_CG */
#include "be_util.h"                /* for Reset_Current_PU_Count(), etc */
#include "wn.h"			    /* for WN */
#include "driver_util.h"	    /* for Process_Command_Line() */
#include "timing.h"		    /* for Reset_Timer(), etc. */
#include "glob.h"		    /* for Show_Progress */
#include "stab.h"		    /* for ir_bread.h */
#include "pu_info.h"		    /* for PU_Info */
#include "ir_bread.h"		    /* for Read_Global_Info() */
#include "ir_bwrite.h"		    /* for Write_Global_Info(), etc. */
#include "config.h"		    /* for LNO_Path, etc. */
#include "config_opt.h"             /* for Instrumentation_Enabled */
#include "config_list.h"	    /* for List_Enabled, etc. */
#include "config_lno.h"		    /* for LNO_Run_Lego, etc. */
#include "file_util.h"		    /* for New_Extension () */
#include "xstats.h"		    /* for Print_Stats () */
#include "data_layout.h"	    /* for Initialize_Stack_Frame() */
#include "opt_alias_interface.h"    /* for ALIAS_MANAGER stuff */
#include "wn_lower.h"		    /* for WN_Lower() */
#include "cgdriver.h"		    /* for CG_Init, etc. */
#include "optimizer.h"		    /* for alias manager, etc. */
#include "ori.h"		    /* for Olimit_Region_Insertion */
#include "wodriver.h"		    /* for wopt_main, etc. */
#include "lnodriver.h"		    /* for lno_main, etc. */
#include "ipl_driver.h"		    /* for ipl_main. etc. */
#include "w2c_driver.h"		    /* for W2C_Process_Command_Line, etc. */
#include "w2f_driver.h"		    /* for W2F_Process_Command_Line, etc. */
#include "prp_driver.h"		    /* for Prp_Process_Command_Line, etc. */
#include "anl_driver.h"		    /* for Anl_Process_Command_Line, etc. */
#include "region_util.h"	    /* for Regions_Around_Inner_Loops */
#include "region_main.h"	    /* for REGION_* driver specific routines */
#include "cg/cg.h"	            /* for CG PU-level routines */
#include "tracing.h"                /* For the trace routines */
#include "ir_reader.h"              /* For fdump_tree */
#include "dwarf_DST.h"	    	    /* for Orig_PU_Name */
#include "fb_whirl.h"		    /* for FEEDBACK */
#include "eh_region.h"		    /* for EH_Generate_Range_List, etc. */
#include "vho_lower.h"
#include "iter.h"		    /* PU iterator for loops */
#include "dra_export.h"             /* for DRA routines */
#include "ti_init.h"		    /* for targ_info */
#include "opt_alias_interface.h"    /* for Create_Alias_Manager */
#include "omp_lower.h"              /* for OMP pre-lowering interface */
#include "cxx_memory.h"		    /* CXX_NEW */
#include "options_stack.h"	    /* for Options_Stack */
#include "be_symtab.h"		    /* for Be_preg_tab */
#include "prompf.h" 		    /* Prompf support */ 
#include "wb_omp.h"		    /* whirl browser for omp prelowerer */
#include "wb_lwr.h"		    /* whirl browser for lowerer */ 
#include "wb_anl.h"		    /* whirl browser for prompf static anal */ 
#include "wn_instrument.h"          /* whirl instrumenter */
#include "mem_ctr.h"


#if defined(__CYGWIN__)
# define DSOext ".dll" /* cygwin needs to use dll for DSOs */
#else
# define DSOext ".so"
#endif


extern void Initialize_Targ_Info(void);

#if defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)
extern "C" {
INT64       New_Construct_Id(void) { return 0; }
INT64       Get_Next_Construct_Id(void) { return 0; }
}
#endif


// symbols defined in cg.so
#if defined(__linux__) || defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)

extern void (*CG_Process_Command_Line_p) (INT, char **, INT, char **);
#define CG_Process_Command_Line (*CG_Process_Command_Line_p)

extern void (*CG_Init_p) ();
#define CG_Init (*CG_Init_p)

extern void (*CG_Fini_p) ();
#define CG_Fini (*CG_Fini_p)

extern void (*CG_PU_Initialize_p) (WN*);
#define CG_PU_Initialize (*CG_PU_Initialize_p)

extern void (*CG_PU_Finalize_p) ();
#define CG_PU_Finalize (*CG_PU_Finalize_p)

extern WN* (*CG_Generate_Code_p) (WN*, ALIAS_MANAGER*, DST_IDX, BOOL);
#define CG_Generate_Code (*CG_Generate_Code_p)

extern void (*EH_Generate_Range_List_p) (WN *);
#define EH_Generate_Range_List (*EH_Generate_Range_List_p)

#else

#pragma weak CG_Process_Command_Line
#pragma weak CG_Init
#pragma weak CG_Fini
#pragma weak CG_PU_Finalize
#pragma weak CG_PU_Initialize
#pragma weak CG_Generate_Code
#pragma weak EH_Generate_Range_List

#endif // __linux__


// symbols defined in wopt.so
#if defined(__linux__) || defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)

extern void (*wopt_main_p) (INT argc, char **argv, INT, char **);
#define wopt_main (*wopt_main_p)

extern void (*Wopt_Init_p) ();
#define Wopt_Init (*Wopt_Init_p)

extern void (*Wopt_Fini_p) ();
#define Wopt_Fini (*Wopt_Fini_p)

extern WN* (*Perform_Preopt_Optimization_p) (WN *, WN *);
#define Perform_Preopt_Optimization (*Perform_Preopt_Optimization_p)

extern WN* (*Perform_Global_Optimization_p) (WN *, WN *, ALIAS_MANAGER *);
#define Perform_Global_Optimization (*Perform_Global_Optimization_p)

extern WN* (*Pre_Optimizer_p) (INT32, WN*, DU_MANAGER*, ALIAS_MANAGER*);
#define Pre_Optimizer (*Pre_Optimizer_p)

extern DU_MANAGER* (*Create_Du_Manager_p) (MEM_POOL *);
#define Create_Du_Manager (*Create_Du_Manager_p)

extern void (*Delete_Du_Manager_p) (DU_MANAGER *, MEM_POOL *);
#define Delete_Du_Manager (*Delete_Du_Manager_p)

extern BOOL (*Verify_alias_p) (ALIAS_MANAGER *, WN *);
#define Verify_alias (*Verify_alias_p)

#else

#pragma weak wopt_main
#pragma weak Wopt_Init
#pragma weak Wopt_Fini
#pragma weak Perform_Global_Optimization
#pragma weak Perform_Preopt_Optimization
#pragma weak Pre_Optimizer
#pragma weak Create_Du_Manager
#pragma weak Delete_Du_Manager
#pragma weak Verify_alias

#endif // __linux__


// symbols defined in lno.so
#if defined(__linux__) || defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)

extern void (*lno_main_p) (INT, char**, INT, char**);
#define lno_main (*lno_main_p)

extern void (*Lno_Init_p) ();
#define Lno_Init (*Lno_Init_p)

extern void (*Lno_Fini_p) ();
#define Lno_Fini (*Lno_Fini_p)

extern WN* (*Perform_Loop_Nest_Optimization_p) (PU_Info*, WN*, WN*, BOOL);
#define Perform_Loop_Nest_Optimization (*Perform_Loop_Nest_Optimization_p)

#else 

#pragma weak lno_main
#pragma weak Lno_Init
#pragma weak Lno_Fini
#pragma weak Perform_Loop_Nest_Optimization

#endif // __linux__


// symbols defined in ipl.so
#if defined(__linux__) || defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)

extern void (*Ipl_Extra_Output_p) (Output_File *);
#define Ipl_Extra_Output (*Ipl_Extra_Output_p)

extern void (*Ipl_Init_p) ();
#define Ipl_Init (*Ipl_Init_p)

extern void (*Ipl_Fini_p) ();
#define Ipl_Fini (*Ipl_Fini_p)

extern void (*ipl_main_p) (INT, char **);
#define ipl_main (*ipl_main_p)

extern void (*Perform_Procedure_Summary_Phase_p) (WN*, DU_MANAGER*,
						  ALIAS_MANAGER*, void*);
#define Perform_Procedure_Summary_Phase (*Perform_Procedure_Summary_Phase_p)

#else

#pragma weak ipl_main
#pragma weak Ipl_Init
#pragma weak Ipl_Fini
#pragma weak Ipl_Extra_Output
#pragma weak Perform_Procedure_Summary_Phase

#endif // __linux__


#include "w2c_weak.h"
#include "w2f_weak.h"


#if defined(_GCC_NO_PRAGMAWEAK) || defined(__CYGWIN__)
void        Anl_Process_Command_Line (INT phase_argc, char *phase_argv[],
                                      INT argc, char *argv[]) { }
BOOL        Anl_Needs_Whirl2c(void) { return false; }
BOOL        Anl_Needs_Whirl2f(void) { return false; }
void        Anl_Init(void) { }
WN_MAP      Anl_Init_Map(MEM_POOL *id_map_pool) { return NULL; }
void        Anl_Static_Analysis(WN *pu, WN_MAP id_map) { }
const char *Anl_File_Path(void)        { return NULL; }
void        Anl_Fini(void)   { }
void        Anl_Cleanup(void){ }
void        Prp_Process_Command_Line (INT phase_argc, char *phase_argv[],
                               INT argc, char *argv[]) { }
BOOL        Prp_Needs_Whirl2c(void) { return false; }
BOOL        Prp_Needs_Whirl2f(void) { return false; }
void        Prp_Init(void)          { }
void        Prp_Instrument_And_EmitSrc(WN *pu) { }
void        Prp_Fini(void) { } 
void        Prompf_Emit_Whirl_to_Source(PU_Info* current_pu,
                                        WN* func_nd) { }

#else

#pragma weak Prp_Process_Command_Line
#pragma weak Prp_Needs_Whirl2c
#pragma weak Prp_Needs_Whirl2f
#pragma weak Prp_Init
#pragma weak Prp_Instrument_And_EmitSrc
#pragma weak Prp_Fini
#pragma weak Anl_Cleanup
#pragma weak Anl_Process_Command_Line
#pragma weak Anl_Needs_Whirl2c
#pragma weak Anl_Needs_Whirl2f
#pragma weak Anl_Init
#pragma weak Anl_Init_Map
#pragma weak Anl_Static_Analysis
#pragma weak Anl_Fini

#endif


/* Solaris CC porting
 * Solaris CC #pragma weak can be either followed by one mangled name in
 * string form, or followed by two mangled names. A better way is to 
 * define an Asserta and let these weak functions to equal to Asserta
 */
#if !defined(__GNUC__) && defined(_SOLARIS_SOLARIS)

#pragma weak "__1cbBPrompf_Emit_Whirl_to_Source6FpnHpu_info_pnCWN__v_"

#elif !defined(__GNUC__)
#pragma weak Prompf_Emit_Whirl_to_Source__GP7pu_infoP2WN

#else
#pragma weak Prompf_Emit_Whirl_to_Source__FP7pu_infoP2WN
#endif

extern void Prompf_Emit_Whirl_to_Source(PU_Info* current_pu, WN* func_nd);


static INT ecount = 0;
static BOOL need_wopt_output = FALSE;
static BOOL need_lno_output = FALSE;
static BOOL need_ipl_output = FALSE;
static Output_File *ir_output = 0;

// options stack for PU and region level pragmas
static OPTIONS_STACK *Options_Stack;

static BOOL reset_opt_level = FALSE;
static struct ALIAS_MANAGER *alias_mgr = NULL;

static BOOL Run_Distr_Array = FALSE;
BOOL Run_MemCtr = FALSE;

static BOOL Saved_run_prompf = FALSE; /* TODO: Remove when uses are removed */
static BOOL Saved_run_w2c = FALSE;        /* TODO: Remove */
static BOOL Saved_run_w2f = FALSE;        /* TODO: Remove */
static BOOL Saved_run_w2fc_early = FALSE; /* TODO: Remove */

extern WN_MAP Prompf_Id_Map; /* Maps WN constructs to unique identifiers */

/* Keep track of which optional components are loaded, where we need
 * to do so.
 */
static BOOL   wopt_loaded = FALSE;
extern BOOL   Prompf_anl_loaded; /* Defined in cleanup.c */
extern BOOL   Purple_loaded;     /* Defined in cleanup.c */
extern BOOL   Whirl2f_loaded;    /* Defined in cleanup.c */
extern BOOL   Whirl2c_loaded;    /* Defined in cleanup.c */

extern void *Current_Dep_Graph;
FILE *DFile = stderr;

static void
load_components (INT argc, char **argv)
{
    INT phase_argc;
    char **phase_argv;

    if (Run_cg || Run_lno || Run_autopar) {
      // initialize target-info before cg or lno
      Initialize_Targ_Info();
    }

    if (!(Run_lno || Run_wopt || Run_preopt || Run_cg || 
	  Run_prompf || Run_purple || Run_w2c || Run_w2f 
          || Run_w2fc_early || Run_ipl))
      Run_cg = TRUE;		    /* if nothing is set, run CG */

    if (Run_ipl) {
      Run_lno = Run_wopt = Run_cg = Run_w2fc_early
	= Run_prompf = Run_purple = Run_w2c = Run_w2f = FALSE;
    }

    if (Run_cg) {
      Get_Phase_Args (PHASE_CG, &phase_argc, &phase_argv);
      load_so ("cg" DSOext, CG_Path, Show_Progress);
      CG_Process_Command_Line (phase_argc, phase_argv, argc, argv);
    }

    if (Run_wopt || Run_preopt || Run_lno || Run_autopar) {
      Get_Phase_Args (PHASE_WOPT, &phase_argc, &phase_argv);
      load_so ("wopt" DSOext, WOPT_Path, Show_Progress);
      wopt_main (phase_argc, phase_argv, argc, argv);
      wopt_loaded = TRUE;
    }

    if (Run_ipl) {
      Get_Phase_Args (PHASE_IPL, &phase_argc, &phase_argv);
      load_so ("ipl" DSOext, Ipl_Path, Show_Progress);
      ipl_main (phase_argc, phase_argv);
      Set_Error_Descriptor (EP_BE, EDESC_BE);
    }

    if (Run_lno || Run_autopar) {
      Get_Phase_Args (PHASE_LNO, &phase_argc, &phase_argv);
      load_so ("lno" DSOext, LNO_Path, Show_Progress);
      lno_main (phase_argc, phase_argv, argc, argv);

      // load in ipl.so if we need to perform automatic
      // parallelization and interprocedural analysis has
      // been performed
      if (Run_autopar && LNO_IPA_Enabled) {
	  load_so("ipl" DSOext, Ipl_Path, Show_Progress);
      }
  }

    if (Run_prompf || Run_w2fc_early) {
      Get_Phase_Args (PHASE_PROMPF, &phase_argc, &phase_argv);
      load_so("prompf_anl" DSOext, Prompf_Anl_Path, Show_Progress);
      Prompf_anl_loaded = TRUE;
      Anl_Process_Command_Line(phase_argc, phase_argv, argc, argv);
    }

    if (Run_purple) {
      Get_Phase_Args (PHASE_PURPLE, &phase_argc, &phase_argv);
      load_so("purple" DSOext, Purple_Path, Show_Progress);
      Purple_loaded = TRUE;
      Prp_Process_Command_Line(phase_argc, phase_argv, argc, argv);
    }

    if (Run_w2c || 
	(Run_prompf && Anl_Needs_Whirl2c()) ||
	(Run_purple && Prp_Needs_Whirl2c()))
    {
      Get_Phase_Args (PHASE_W2C, &phase_argc, &phase_argv);
      load_so("whirl2c" DSOext, W2C_Path, Show_Progress);
      Whirl2c_loaded = TRUE;
      if (Run_prompf)
	W2C_Set_Prompf_Emission(&Prompf_Id_Map);
      W2C_Process_Command_Line(phase_argc, phase_argv, argc, argv);
    }

    if (Run_w2f || 
	(Run_prompf && Anl_Needs_Whirl2f()) ||
	(Run_purple && Prp_Needs_Whirl2f()))
    {
      Get_Phase_Args (PHASE_W2F, &phase_argc, &phase_argv);
      load_so("whirl2f" DSOext, W2F_Path, Show_Progress);
      Whirl2f_loaded = TRUE;
      if (Run_prompf)
	W2F_Set_Prompf_Emission(&Prompf_Id_Map);

      W2F_Process_Command_Line(phase_argc, phase_argv, argc, argv);
    }
} /* load_components */


/* phase-specific initializations that need to be done after reading
 * in the global symbol tables.
 */
static void
Phase_Init (void)
{
    char *output_file_name = Obj_File_Name;

    if (Run_Distr_Array      && 
        (Run_w2c || Run_w2f) &&
        !Run_lno             &&
        !Run_wopt            &&
        !Run_cg)
    {
       /* A special case, where it looks as though we only wish to
        * run some early phases and then put out the flist or clist.
        * Disable the turning on of subsequent phases due to the
        * Run_Distr_Array flag.
        */
       Run_Distr_Array = FALSE;
    }
    if ( LNO_Run_Lego_Set && ( LNO_Run_Lego == FALSE ) )
      Run_Distr_Array = FALSE;

    if (Run_cg)
	CG_Init ();
    if (Run_wopt || Run_preopt)
	Wopt_Init ();
    if (Run_ipl)
	Ipl_Init ();
    if (Run_lno || Run_Distr_Array || Run_autopar)
	Lno_Init ();
    if (Run_purple)
	Prp_Init();
    if (Run_w2c || (Run_prompf && Anl_Needs_Whirl2c()))
	W2C_Outfile_Init (TRUE/*emit_global_decls*/);
    if (Run_w2f || (Run_prompf && Anl_Needs_Whirl2f()))
	W2F_Outfile_Init ();
    if (Run_prompf) 
	Anl_Init (); 
	/* Must be done after w2c and w2f */
    if ((Run_lno || Run_preopt) && !Run_cg && !Run_wopt)
	need_lno_output = TRUE;
    if (Run_wopt && !Run_cg)
	need_wopt_output = TRUE;

    if (Run_ipl) {
	need_ipl_output = TRUE;
	need_lno_output = need_wopt_output = FALSE;
    }

    if (output_file_name == 0) {
	if (Src_File_Name)
	    output_file_name = Last_Pathname_Component (Src_File_Name);
	else
	    output_file_name = Irb_File_Name;
    }

    if (need_lno_output) {
	Write_BE_Maps = TRUE;
	ir_output = Open_Output_Info(New_Extension(output_file_name,".N"));
    }
    if (need_wopt_output) {
	Write_ALIAS_CLASS_Map = TRUE;
	Write_BE_Maps = TRUE;
	ir_output = Open_Output_Info(New_Extension(output_file_name,".O"));
    }
    if (need_ipl_output) {
	Write_BE_Maps = FALSE;
	ir_output = Open_Output_Info (Obj_File_Name ?
				      Obj_File_Name :
				      New_Extension(output_file_name, ".o"));
    }
    if (Emit_Global_Data) {
	Write_BE_Maps = FALSE;
	ir_output = Open_Output_Info (Global_File_Name);
    }
	
    if (Run_wopt) {
      if (Language != LANG_KR_C) {
        Pad_Global_Arrays();
      }
    }
    if ((Run_cg || Run_wopt) && !Read_Global_Data)
	Allocate_File_Statics();	/* allocate globals */

} /* Phase_Init */


static void
Phase_Fini (void)
{
    CURRENT_SYMTAB = GLOBAL_SYMTAB;

    /* Always finish prompf analysis file, purple, w2c and w2f first */
    if (Run_prompf)
	Anl_Fini();
    if (Run_purple)
	Prp_Fini();
    if (Run_w2c || (Run_prompf && Anl_Needs_Whirl2c()))
	W2C_Outfile_Fini (TRUE/*emit_global_decls*/);
    if (Run_w2f || (Run_prompf && Anl_Needs_Whirl2f()))
	W2F_Outfile_Fini ();

    if (Run_Dsm_Cloner || Run_Dsm_Common_Check)
      DRA_Finalize ();

    if (Run_lno || Run_Distr_Array || Run_autopar)
	Lno_Fini ();
    if (Run_ipl)
	Ipl_Fini ();
    if (Run_wopt || Run_preopt)
	Wopt_Fini ();
    if (Run_cg)
	CG_Fini ();

    Verify_SYMTAB (CURRENT_SYMTAB); /* Verifies global SYmtab */
} /* Phase_Fini */

/* static */ char *
Get_Orig_PU_Name (PU_Info * current_pu)
{
    DST_IDX dst;
    DST_INFO *info;
    DST_SUBPROGRAM *PU_attr;

    dst = PU_Info_pu_dst(current_pu);

    if (DST_IS_NULL (dst)) {
       return ST_name(PU_Info_proc_sym(current_pu));
    }

    info = DST_INFO_IDX_TO_PTR (dst);

    if ( (DST_INFO_tag(info) != DW_TAG_subprogram)
        || DST_IS_declaration(DST_INFO_flag(info)) )
    {
	return ST_name(PU_Info_proc_sym(current_pu));
    }
    PU_attr = DST_ATTR_IDX_TO_PTR(DST_INFO_attributes(info), DST_SUBPROGRAM);
    if (PU_attr->def.name.byte_idx < 0) {
      return NULL;
      /* Why not the following line instead? -- RK 960808
       * return ST_name(PU_Info_proc_sym(current_pu));
       */
    }
    return DST_STR_IDX_TO_PTR(DST_SUBPROGRAM_def_name(PU_attr));
}

static void
Save_Cur_PU_Name (char *name, INT rid_id)
{
    if ( Cur_PU_Name == NULL ) {
	/* ST_name will return a pointer into the symbol table, which is
	 * mmap-ed. This causes a problem in the error message routines
	 * when an unexpected signal occurs, because as part of the cleanup
	 * files are closed. To fix the problem we just allocate some
	 * memory and make a copy.
	 * Allocate 8 extra bytes to leave room for RGN suffix.
	 */
	Cur_PU_Name = TYPE_MEM_POOL_ALLOC_N(char, &MEM_pu_nz_pool, 
		strlen(name) + 8);
	Cur_PU_Name = strcpy(Cur_PU_Name, name);
    }
    if (rid_id != 0) {
	/* add RGN suffix */
	sprintf(Cur_PU_Name,"%s.RGN%03d", name, rid_id);
    }
    else if (strlen(name) != strlen(Cur_PU_Name)) {
	/* clear RGN suffix */
	Cur_PU_Name = strcpy(Cur_PU_Name, name);
    }
}


//  Adjust/Lower optimization level based on
//   1. size of PU and Olimit
//   2. existence of non-ANSI setjmp calls
//
static WN *
Adjust_Opt_Level (PU_Info* current_pu, WN *pu, char *pu_name)
{
    INT new_opt_level = 0;
    COMPUTE_PU_OLIMIT;

    if (Get_Trace(TKIND_INFO, TINFO_STATS)) {
	/* Print Olimit stats to trace file: */
	INT PU_Var_Cnt = ST_Table_Size (CURRENT_SYMTAB) +
	    PREG_Table_Size (CURRENT_SYMTAB); 
	fprintf (TFile, "PU_Olimit for %s is %d (bbs=%d,stms=%d,vars=%d)\n", 
		pu_name, PU_Olimit, PU_WN_BB_Cnt, PU_WN_Stmt_Cnt, PU_Var_Cnt);
    }

    if ((Opt_Level > 0 || Run_autopar) && PU_Olimit > Olimit && !Olimit_opt) {
	if (Show_OPT_Warnings)
	  ErrMsg (EC_Olimit_Exceeded, pu_name, PU_Olimit);
	reset_opt_level = TRUE;
    }
    if (((Opt_Level > 0 || Run_autopar) || Olimit_opt)
      && Query_Skiplist ( Optimization_Skip_List, Current_PU_Count() ) )
    {
	if (Show_OPT_Warnings)
	  ErrMsg (EC_Not_Optimized, pu_name, Current_PU_Count() );
	reset_opt_level = TRUE;
    } 
    if (/* !LANG_Ansi_Setjmp_On && */ 
      /* 1. Cannot check LANG_Ansi_Setjmp_On because IPA does not pass -LANG group.
	 2. The ST_pu_calls_setjmp is not set unless LANG_Ansi_Setjmp_On = false */
	PU_calls_setjmp (Get_Current_PU ())) {
      reset_opt_level = TRUE;
      new_opt_level = 1;
      ErrMsg (EC_Not_Ansi_Setjmp, pu_name, Current_PU_Count(), new_opt_level );
    } 
    if (reset_opt_level) {
	Opt_Level = new_opt_level;
	Run_lno = Run_preopt = Run_wopt = Run_autopar = FALSE;
	alias_mgr = NULL;
	Olimit_opt = FALSE;
        if (Run_prompf) 
	  Prompf_Emit_Whirl_to_Source(current_pu, pu);
    }

    if ((PU_Olimit > Olimit) && Olimit_opt) {
      /* split into regions (ORI) */
      pu = Olimit_Region_Insertion (pu, Olimit);
      /* 457243, for 7.2 throttle back LNO when Olimit is reached.
       * For 7.2 LNO runs on the PU, for 7.3 it will run on regions.
       * options: -LNO:ou=1:fusion=0
       * variables: UINT32 Outer_unroll = 1, UINT32 Fusion = 0
       * common/com/config_lno.h
       */
      if (Run_lno || Run_Distr_Array || Run_preopt || Run_autopar) {
	LNO_Outer_Unroll = 1;
	LNO_Fusion = 0;
	if (Show_OPT_Warnings)
	  ErrMsg(EC_LNO_Backoff, pu_name, LNO_Outer_Unroll, LNO_Fusion);
      }
    }

    return pu;
} /* Adjust_Opt_Level */

static void
Ipl_Processing (PU_Info *current_pu, WN *pu)
{
    struct DU_MANAGER *du_mgr = NULL;
    struct ALIAS_MANAGER *al_mgr = NULL;

    MEM_POOL_Push (&MEM_local_pool);

    PU_adjust_addr_flags(Get_Current_PU_ST(), pu);

    if (Run_preopt) {
	du_mgr = Create_Du_Manager(MEM_pu_nz_pool_ptr);
	al_mgr = Create_Alias_Manager(MEM_pu_nz_pool_ptr);
	pu = Pre_Optimizer(PREOPT_IPA0_PHASE, pu, du_mgr, al_mgr);
#ifdef Is_True_On
	if (Get_Trace (TKIND_ALLOC, TP_IPA)) {
	    fprintf (TFile, "\n%s%s\tMemory allocation information after"
		     " IPA local pre_opt\n%s%s\n", DBar, DBar, DBar, DBar);
	    MEM_Trace ();
	}
#endif
	Delete_Alias_Manager (al_mgr, MEM_pu_nz_pool_ptr);
	Delete_Du_Manager (du_mgr, MEM_pu_nz_pool_ptr);
    } else {
	Set_Error_Phase ( "IPA Summary" );
	Perform_Procedure_Summary_Phase (pu, du_mgr, al_mgr, 0);
    }

    /* Write out the current proc */
    Set_PU_Info_tree_ptr(current_pu, pu);

    Write_PU_Info (current_pu);

    MEM_POOL_Pop (&MEM_local_pool);
    
} /* Ipl_Processing */

/*====================================================================*/
/* Compilation time loop: LNO			 		      */
/*====================================================================*/
static WN *
LNO_Processing (PU_Info *current_pu, WN *pu)
{
  REGION_CS_ITER rgn_iter;

  REGION_CS_ITER_init(&rgn_iter, pu);
  /* PV 457243 for Mongoose 7.2 do just the PU (RID_TYPE_func_entry),
     for Mongoose 7.3 use: RID_TYPE_olimit | RID_TYPE_pragma
     These keeps regions out of LNO for 7.2 */
  for (REGION_CS_NoEarlierSub_First(&rgn_iter, pu, RID_TYPE_func_entry);
       REGION_CS_NoEarlierSub_While(&rgn_iter);
       REGION_CS_NoEarlierSub_Next(&rgn_iter)) {

    WN *rwn;

    rwn = REGION_remove_and_mark(pu, &rgn_iter);
    Is_True(rwn != NULL,
	    ("BE driver, IR inconsistency, LNO loop"));
    if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
      fprintf(TFile,"===== BE driver, LNO loop: %s %d, stacked=%d\n",
	      REGION_CS_ITER_is_pu(&rgn_iter) ? "PU" : "RGN",
	      RID_id(REGION_get_rid(rwn)),
	      !REGION_CS_ITER_is_not_stacked(&rgn_iter));
      RID_WN_Tree_Print(TFile, rwn);
    }

    /* Don't (re-)run LNO or preopt on nested mp PU's */
    /* unless early mp processing was requested.  In which case */
    /* LNO and preopt haven't been run on the nexted mp PU's yet. */
    BOOL is_mp = PU_mp (Get_Current_PU ());
    BOOL needs_lno = PU_mp_needs_lno (Get_Current_PU ());

    if (!is_mp || Early_MP_Processing) {
      if (Run_lno || Run_autopar || (Run_Distr_Array && needs_lno)) {

	if (Early_MP_Processing && is_mp) {
          Free_Dep_Graph(); 
	  Current_Dep_Graph = PU_Info_depgraph_ptr(Current_PU_Info);
	}
	rwn = Perform_Loop_Nest_Optimization(current_pu, pu, rwn, 
          TRUE /*can use alloca*/);
	Set_PU_Info_depgraph_ptr(Current_PU_Info,Current_Dep_Graph);
	if (Current_Dep_Graph) {
	  Set_PU_Info_state(Current_PU_Info,WT_DEPGRAPH,Subsect_InMem);
        } else {
	  Set_PU_Info_state(Current_PU_Info,WT_DEPGRAPH,Subsect_Missing);
        }

	Check_for_IR_Dump(TP_LNOPT, rwn, "LNO");
	if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	  fprintf(TFile,"===== After LNO, %s %d, stacked=%d\n",
		  REGION_CS_ITER_is_pu(&rgn_iter) ? "PU" : "RGN",
		  RID_id(REGION_get_rid(rwn)),
		  !REGION_CS_ITER_is_not_stacked(&rgn_iter));
	  RID_WN_Tree_Print(TFile, rwn);
	  fdump_tree(TFile, rwn);
	  RID_set_print(TFile,REGION_get_rid(rwn));
	}
      } else if (Run_preopt) {
	rwn = Perform_Preopt_Optimization(pu, rwn);
	Check_for_IR_Dump(TP_GLOBOPT, rwn, "PREOPT");
	if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	  fprintf(TFile,"===== After PREOPT (:p), %s %d, stacked=%d\n",
		  REGION_CS_ITER_is_pu(&rgn_iter) ? "PU" : "RGN",
		  RID_id(REGION_get_rid(rwn)),
		  !REGION_CS_ITER_is_not_stacked(&rgn_iter));
	  RID_WN_Tree_Print(TFile, rwn);
	  fdump_tree(TFile, rwn);
	  RID_set_print(TFile,REGION_get_rid(rwn));
	}
      }
    } else {
      Free_Dep_Graph(); 
      Current_Dep_Graph = PU_Info_depgraph_ptr(Current_PU_Info);
    }

    if (REGION_CS_ITER_is_not_stacked(&rgn_iter)) /* this is tricky */
      pu = rwn;
    REGION_replace_from_mark(rwn, &rgn_iter);

    if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
      fprintf(TFile,"------------ bottom of LNO loop: -----------\n");
      REGION_consistency_check(pu);
      RID_WN_Tree_Print(TFile,pu);
      if (Run_cg)
	fdump_region_tree(TFile,pu);
      else
	fdump_tree(TFile,pu);
      fprintf(TFile,"--------------------------------------------\n");
    }
  } /* end of compilation time region loop */

  return pu;
} /* LNO_Processing */


/* Misc. processing after LNO is done, PU exists as a whole during this
   procedure */
static void
Post_LNO_Processing (PU_Info *current_pu, WN *pu)
{
    BOOL is_user_visible_pu = (CURRENT_SYMTAB == GLOBAL_SYMTAB + 1) || 
                              ((Language == LANG_F90) &&
			       (CURRENT_SYMTAB == GLOBAL_SYMTAB + 2) &&
			       (!Is_Set_PU_Info_flags(current_pu, PU_IS_COMPILER_GENERATED))) ;
    
    /* Only run w2c and w2f on top-level PUs, unless otherwise requested.
     */
    if (Run_w2c && !Run_w2fc_early && !Run_prompf) {
	if (W2C_Should_Emit_Nested_PUs() || is_user_visible_pu) {
	    if (Cur_PU_Feedback)
		W2C_Set_Frequency_Map(WN_MAP_FEEDBACK);
	    W2C_Outfile_Translate_Pu(pu, TRUE/*emit_global_decls*/);
	}
    }
    if (Run_w2f && !Run_w2fc_early && !Run_prompf) {
	if (W2F_Should_Emit_Nested_PUs() || is_user_visible_pu) {
	    if (Cur_PU_Feedback)
		W2F_Set_Frequency_Map(WN_MAP_FEEDBACK);
	    W2F_Outfile_Translate_Pu(pu);
	}
    }

    /* only write .N file for PU, no need to replace region because
       REGION_remove_and_mark does nothing for pu (rwn is the pu) */
    if (need_lno_output) {
	Set_PU_Info_tree_ptr(current_pu, pu);
	Write_PU_Info(current_pu);
	Verify_SYMTAB (CURRENT_SYMTAB);
    }

} /* Post_LNO_Processing */

static WN *
WOPT_Processing (PU_Info *current_pu, WN *pu, REGION_CS_ITER *rgn_iter,
		 WN *rwn)
{
    rwn = Perform_Global_Optimization (pu, rwn, alias_mgr);
    Check_for_IR_Dump(TP_GLOBOPT, rwn, "WOPT");
    if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
      fprintf(TFile, "===== driver, after Perform_Global_Optimization");
      fprintf(TFile," RGN %d =====\n", RID_id(REGION_get_rid(rwn)));
      RID_WN_Tree_Print(TFile,pu);
      fdump_tree(TFile,rwn);
      fprintf(TFile,"===== driver ---------------------\n");
      fprintf(TFile,"#### Verify_alias, after mainopt, RGN %d\n",
	      RID_id(REGION_get_rid(rwn)));
      Verify_alias(alias_mgr, rwn);
      if (Run_cg) /* cg has to be loaded to run this routine */
	fdump_region_tree(TFile,rwn);
      fprintf(TFile,"#### Verify_alias, done\n");
    }

    return rwn;
} /* WOPT_Processing */

// Performance region loop WOPT-CG
static WN *
Do_WOPT_and_CG_with_Regions (PU_Info *current_pu, WN *pu)
{
    REGION_CS_ITER rgn_iter;
    BOOL Run_region_bounds;
    BOOL cg_one_time = TRUE;

    /* look over whole PU and set up stack model */
    Initialize_Stack_Frame (pu);	

    // decide whether the PU has regions that need bounds in it
    { RID *rid = REGION_get_rid(pu);
      Is_True(rid != NULL && RID_TYPE_func_entry(rid) && RID_id(rid) == 0,
	      ("Do_WOPT_and_CG_with_Regions, RID is incorrect"));
      Run_region_bounds = RID_contains_bounds(rid);
      Is_Trace(Get_Trace(TP_REGION, TT_REGION_CG_DEBUG) ||
	       Get_Trace(TP_REGION, TT_REGION_BOUND_DEBUG),
	       (TFile,"Driver, RID_contains_bounds(%s) = %c\n",
		ST_name(PU_Info_proc_sym(current_pu)),
		Run_region_bounds ? 'T' : 'F'));
    }

    // One alias_mgr for all regions (both cg and wopt need it).
    // this is the alias manager for all region instances of
    // MainOpt and CG. Preopt uses it's own local alias manager,
    // one for each region. Deleted by Post_Process_PU.
    if ((Run_wopt || Run_region_bounds) && alias_mgr == 0)
      alias_mgr = Create_Alias_Manager(MEM_pu_nz_pool_ptr);

    // Create the region boundary sets, be/region/region_bounds.cxx
    if (Run_region_bounds) {
      Set_Error_Phase("Generate Region Boundaries");
      Generate_region_boundaries(pu, alias_mgr);
    }

    // region loop
    // 1) This iterator first finds the enclosing block for each region so
    //    the region can be replaced once it it processed.
    // 2) REGION_remove_and mark pulls the region out of the WHIRL tree
    //    so we can process it independently, put back by
    //    REGION_replace_from_mark
    // 3) Process_pragma_options changes the compile options based on region
    //    pragmas in the code.
    
    REGION_CS_ITER_init(&rgn_iter, pu);
    for (REGION_CS_NoEarlierSub_First(&rgn_iter, pu,
	   (RID_TYPE)(RID_TYPE_olimit | RID_TYPE_pragma | RID_TYPE_loop));
	 REGION_CS_NoEarlierSub_While(&rgn_iter);
	 REGION_CS_NoEarlierSub_Next(&rgn_iter)) {
      WN *rwn;
      BOOL need_options_pop;

      rwn = REGION_remove_and_mark(pu, &rgn_iter);

      Is_True(rwn != NULL, ("BE driver, IR inconsistency, WOPT-CG loop"));
      Is_True(REGION_get_rid(rwn) != NULL, ("BE driver, NULL RID"));
      Is_True(RID_type(REGION_get_rid(rwn)) != RID_TYPE_undefined,
	      ("BE driver, undefined RID type"));

      Set_Current_Region_For_Trace( RID_id(REGION_get_rid(rwn)) );

      if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	fprintf(TFile,"===== BE driver, WOPT-CG loop: %s %d, stacked=%d\n",
		REGION_CS_ITER_is_pu(&rgn_iter) ? "PU" : "RGN",
		RID_id(REGION_get_rid(rwn)),
		!REGION_CS_ITER_is_not_stacked(&rgn_iter));
	RID_WN_Tree_Print(TFile, rwn);
      }

      // process any region or PU level options pragmas
      { RID *rid = REGION_get_rid(rwn);
	Is_True(rid != NULL, ("BE driver, NULL rid inside region loop"));
	if (RID_options(rid) != NULL) {
	  Options_Stack->Process_Pragma_Options(RID_options(rid));
	  need_options_pop = TRUE;
	} else
	  need_options_pop = FALSE;
      }

      if (Show_Progress) {
	if (rwn != pu)
	  fprintf(stderr, "... compiling region (%d)\n", 
		  RID_id(REGION_get_rid(rwn)));
	else if (!cg_one_time)	/* must have had regions in pu */
	  fprintf(stderr, "... compiling program unit\n");
      }
      /* if we are processing a region, add that to the name */
      Save_Cur_PU_Name (ST_name(PU_Info_proc_sym(current_pu)), 
			RID_id(REGION_get_rid(rwn)));

      /* Add instrumentation here for wopt. */
      if (Instrumentation_Enabled
	  && Instrumentation_Phase_Num == PROFILE_PHASE_BEFORE_WOPT) {
	WN_Instrument(rwn, PROFILE_PHASE_BEFORE_WOPT); 
      } else if (Feedback_Enabled[PROFILE_PHASE_BEFORE_WOPT]) {
	WN_Annotate(rwn, PROFILE_PHASE_BEFORE_WOPT, &MEM_pu_pool);
      }
      Set_Error_Phase ( "Before WOPT" );

      if (Run_wopt) {
	rwn = WOPT_Processing (current_pu, pu, &rgn_iter, rwn);
        if (WN_opcode(rwn) == OPC_FUNC_ENTRY)
          Verify_SYMTAB (CURRENT_SYMTAB);
	if ( Cur_PU_Feedback ) {
	  Cur_PU_Feedback->Verify("after WOPT");
	}
#if 0  // this is now unnecessary because the lowering is done inside wopt
	if (Only_Unsigned_64_Bit_Ops && Delay_U64_Lowering)
	  U64_lower_wn(rwn, FALSE);
#endif
      }

      /* we may still have to print the .O file:		   */
      /* (Olimit stops optimization for one PU but not all)	   */
      /* only write .O file for PU, no need to replace region	   */
      /* because REGION_remove_and_mark does nothing for pu	   */
      if (need_wopt_output && REGION_CS_ITER_is_not_stacked(&rgn_iter)) {
	Set_PU_Info_tree_ptr(current_pu, rwn);
	Write_PU_Info(current_pu);
      }


      /* in case wopt cleared it (temporary backwards-compatibility) */
      Save_Cur_PU_Name (ST_name(PU_Info_proc_sym(current_pu)), 
		RID_id(REGION_get_rid(rwn)));

      /* Add instrumentation here for cg. */
      if (Instrumentation_Enabled
	  && Instrumentation_Phase_Num == PROFILE_PHASE_BEFORE_CG) {
	rwn = WN_Lower(rwn, LOWER_SCF, NULL, 
		       "Lower structured control flow");
	WN_Instrument(rwn, PROFILE_PHASE_BEFORE_CG);
#if 0
	extern void wb_gwe(WN*); // hack to check __profile calls.
	wb_gwe(rwn);
#endif
      } else if (Feedback_Enabled[PROFILE_PHASE_BEFORE_CG]) {
	rwn = WN_Lower(rwn, LOWER_SCF, NULL, 
		       "Lower structured control flow");
	WN_Annotate(rwn, PROFILE_PHASE_BEFORE_CG, &MEM_pu_pool);
      }
      Set_Error_Phase ( "Before CG" );

      if (Run_cg) { /* lower for cg */
	Set_Error_Phase ("Lowering");
        WB_LWR_Initialize(rwn, alias_mgr);
	rwn = WN_Lower(rwn, LOWER_TO_CG, alias_mgr, "Lowering to CG");
	if (Only_Unsigned_64_Bit_Ops && ! Run_wopt)
	  U64_lower_wn(rwn, FALSE);
        WB_LWR_Terminate();
	if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	  fprintf(TFile,"===== driver, after lowering\n");
	  RID_WN_Tree_Print(TFile,rwn);
	}

	if ( Cur_PU_Feedback ) {
	  Cur_PU_Feedback->Verify("after LOWER_TO_CG");
	}

	if (cg_one_time) {  /* do this before processing part of PU */
	  cg_one_time = FALSE;
	  /* move data layout to after whirl lowering	*/
	  Set_Error_Phase("Data Layout");
    	  /* look over whole PU and calculate size of actual area */
    	  Calculate_Stack_Frame_Sizes (rwn);	
	  CG_PU_Initialize(pu);
	}

	if (!REGION_CS_ITER_is_not_stacked(&rgn_iter) ||
	    !REGION_CS_ITER_is_pu(&rgn_iter)) { /* pass over region */
	  DST_IDX tmp_idx;
	  WN *compiled_block;
	  tmp_idx.byte_idx = DST_INVALID_BYTE_IDX; /* mark DST as NULL */
	  FmtAssert(PU_has_region (Get_Current_PU ()),
		    ("BE driver, found region when SYMTAB_has_rgn is off"));
	  if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	    fprintf(TFile,
		    "===== driver, before CG_Generate_Code (region)\n");
	    RID_WN_Tree_Print(TFile,rwn);
	    RID_set_print(TFile,REGION_get_rid(rwn));
	  }
	  compiled_block = CG_Generate_Code(rwn, alias_mgr, tmp_idx, TRUE);
	  rwn = compiled_block;
	  if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	    fprintf(TFile,
		    "===== driver, after CG_Generate_Code\n");
	    RID_WN_Tree_Print(TFile,rwn);
	  }
	} // if (!REGION_CS_ITER_is_not_stacked(&rgn_iter) ||
      } // if (Run_cg)

      if (REGION_CS_ITER_is_not_stacked(&rgn_iter)) /* this is tricky */
	pu = rwn;
      REGION_replace_from_mark(rwn, &rgn_iter);
      // put options back the way they were
      if (need_options_pop)
	Options_Stack->Pop_Current_Options();
      if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
	fprintf(TFile,
		"------------ bottom of WOPT-CG loop: ------------\n");
	Is_True(REGION_consistency_check(pu),(""));
	RID_WN_Tree_Print(TFile,pu);
	if (Run_cg)
	  fdump_region_tree(TFile,pu);
	else
	  fdump_tree(TFile,pu);
	fprintf(TFile,"--------------------------------------------\n");
      }
      if (rwn != pu)
	Report_CG_Region_Timing ( Tim_File, Cur_PU_Name );
    }
    Verify_SYMTAB (CURRENT_SYMTAB);
    return pu;
} /* Do_WOPT_and_CG_with_Regions */


static void
Post_Process_Backend (PU_Info *current_pu, WN *pu)
{
  if (Run_cg) {
    if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
      fprintf(TFile,"BE driver, Post_Process_Backend "
	      "+++++++++++++++++++++++++++++++\n");
      RID_WN_Tree_Print(TFile,pu);
      fdump_region_tree(TFile,pu);
      fprintf(TFile,"BE driver, Post_Process_Backend "
	      "+++++++++++++++++++++++++++++++\n");
      fprintf(TFile,"Right before CG_Generate_Code (PU)\n");
    }

    CG_Generate_Code(pu, alias_mgr, PU_Info_pu_dst(current_pu), FALSE);
    CG_PU_Finalize();
  }
  // Delete alias manager after CG finished, PV 525127, 527977
  if (alias_mgr) {
    Delete_Alias_Manager(alias_mgr, MEM_pu_nz_pool_ptr);
    alias_mgr = NULL;
  }
} /* Post_Process_Backend */



extern "C" {
  extern void Process_Fill_Align_Pragmas (WN* func_wn);
  extern void Rewrite_Pragmas_On_Structs (WN* block_wn, WN* wn);
}

/***********************************************************************
 *
 * Find all EH regions in the PU, and mark their INITOs as used.
 *
 ***********************************************************************/
static void Update_EHRegion_Inito_Used (WN *wn) {
  if (!wn) return;
  
  OPERATOR opr = WN_operator(wn);

  if (opr == OPR_REGION && WN_ereg_supp(wn)) {
    INITO_IDX ino_idx = WN_ereg_supp(wn);
    ST *st = INITO_st(ino_idx);
    Clear_ST_is_not_used(st);
  }

  // now recurse
  if (opr == OPR_BLOCK) {
    WN *kid = WN_first (wn);
    while (kid) {
      Update_EHRegion_Inito_Used(kid);
      kid = WN_next(kid);
    }
  } else {
    for (INT kidno=0; kidno<WN_kid_count(wn); kidno++) {
      Update_EHRegion_Inito_Used(WN_kid(wn,kidno));
    }
  }
}

/***********************************************************************
 *
 * This pass is called after preopt+lno+mplowering. 
 * Any of those passes may have deleted EH-regions, but left the
 * INITO sts for those regions hanging around.
 * This pass will search for all used INITOs, and mark the rest unused.
 *
 ***********************************************************************/
static void Update_EHRegion_Inito (WN *pu) {
  INT i;
  INITO *ino;

  // first mark all EH-region STs unused.
  FOREACH_INITO (CURRENT_SYMTAB, ino, i) {
    ST *st = INITO_st(ino);
    if (ST_sclass(st) == SCLASS_EH_REGION ||
        ST_sclass(st) == SCLASS_EH_REGION_SUPP) {
      Set_ST_is_not_used(st);
    }
  }

  // now find INITO sts that are referenced in WHIRL,
  // and mark them used.
  Update_EHRegion_Inito_Used (pu);
}

static void
Backend_Processing (PU_Info *current_pu, WN *pu)
{
    {
	/* Always process the first PU for fill-align, since that one contains
	 * the pragmas for global symbols. And, because of IPA, we cannot
	 * depend on the SYMTAB_id being 1 for the first PU.
	 */
	static BOOL done_first_pu = FALSE;
	BOOL needs_fill_align_lowering =
	    PU_needs_fill_align_lowering (Get_Current_PU ());
	if (needs_fill_align_lowering || !done_first_pu) {
	    Process_Fill_Align_Pragmas (pu);
	    done_first_pu = TRUE;
	}
    }

    PU_adjust_addr_flags(Get_Current_PU_ST(), pu);

    if (Run_MemCtr)
	MemCtr_Add (pu);

    /* Make sure that RETURN_VAL nodes, Return_Val_Preg references and
       MLDID/MSTID nodes have been lowered. This requires its own pass
       because it may have to go back to change the previous CALL statement
       to add a fake parameter. */
    if (WHIRL_Return_Val_On || WHIRL_Mldid_Mstid_On) {
        Is_True(WHIRL_Return_Val_On && WHIRL_Mldid_Mstid_On,
	        ("-INTERNAL:return_val and -INTERNAL:mldid_mstid must be on the same time"));
	pu = WN_Lower (pu, LOWER_RETURN_VAL | LOWER_MLDID_MSTID, NULL,
		       "RETURN_VAL & MLDID/MSTID lowering");
    }

    /* If early mp processing has been requested, then do it before running
       lno/preopt. */
    BOOL has_mp = PU_has_mp (Get_Current_PU ());
    if (has_mp && Early_MP_Processing) {
	Set_PU_Info_depgraph_ptr(Current_PU_Info,Current_Dep_Graph);
	Set_PU_Info_state(Current_PU_Info,WT_DEPGRAPH,Subsect_InMem);

	Set_Error_Phase ( "MP Lowering" );
        WB_LWR_Initialize(pu, NULL);
	pu = WN_Lower (pu, LOWER_MP, NULL, "Before MP Lowering");
        WB_LWR_Terminate();
    }

    /* Add instrumentation here for lno. */
    if( Instrumentation_Enabled
       && Instrumentation_Phase_Num == PROFILE_PHASE_BEFORE_LNO ) {
	WN_Instrument(pu, PROFILE_PHASE_BEFORE_LNO); 
    } else if ( Feedback_Enabled[PROFILE_PHASE_BEFORE_LNO] ) {
      WN_Annotate(pu, PROFILE_PHASE_BEFORE_LNO, &MEM_pu_pool);   
    }
    Set_Error_Phase ( "LNO Processing" );

    if (Run_lno || Run_Distr_Array || Run_preopt || Run_autopar) {
	pu = LNO_Processing (current_pu, pu);/* make -O0, -O1, -O2 a bit faster*/
	if (Run_autopar)
	    Rewrite_Pragmas_On_Structs (NULL, WN_func_body(pu));
    }

    /* First round output (.N file, w2c, w2f, etc.) */
    Set_Error_Phase ( "Post LNO Processing" );
    Post_LNO_Processing (current_pu, pu);
    if (!Run_wopt && !Run_cg) return;

    Verify_SYMTAB (CURRENT_SYMTAB);

    /* If no early mp processing has been requested, then do it after running
     * lno/preopt. Do this whether or not wopt is to be run.
     * If we were just running LNO,
     * then the above call to Post_LNO_Processing has written out the
     * WHIRL file and thereby mangled current_pu, so we cannot do the
     * MP lowering.
     * To support an option to see post-LNO, post MP-lowered output,
     * maybe we can just move the call to the lowerer before
     * Post_LNO_Processing.
     */
    has_mp = PU_has_mp (Get_Current_PU ());
    if (has_mp && !Early_MP_Processing) {
	Set_Error_Phase ( "MP Lowering" );
        WB_LWR_Initialize(pu, NULL);
	pu = WN_Lower (pu, LOWER_MP, NULL, "Before MP Lowering");
        WB_LWR_Terminate();
    }

    Update_EHRegion_Inito (pu);

    /* Generate EH range table for PU.  The high and low labels are
     * filled in during code generation.
     */
    if (Run_cg) 
	EH_Generate_Range_List(pu);

    BOOL save_run_wopt = Run_wopt;
    // I believe I can assert that WN_operator(pu) == OPR_FUNC_ENTRY
    // here, but I won't, simply because I don't need to. Might as
    // well check. -- RK 990713
    if (Run_wopt &&
	(WN_operator(pu) == OPR_FUNC_ENTRY) &&
	ST_asm_function_st(*WN_st(pu))) {
      // Throttle WOPT; we don't want it for PU's that merely wrap up
      // file-scope asm statements.
      Run_wopt = FALSE;
    }

    Set_Error_Phase ( "WOPT/CG Processing" );
    pu = Do_WOPT_and_CG_with_Regions (current_pu, pu);

    Run_wopt = save_run_wopt;

    Set_Error_Phase ( "Post Backend Processing" );
    Post_Process_Backend (current_pu, pu);

} /* Backend_Processing */

static WN *
Preprocess_PU (PU_Info *current_pu)
{
  WN *pu = NULL;

  Initialize_PU_Stats ();  /* Needed for Olimit as well as tracing */

  Current_PU_Info = current_pu;
  MEM_POOL_Push(MEM_pu_nz_pool_ptr);
  MEM_POOL_Push(MEM_pu_pool_ptr);

  Cur_PU_Feedback    = NULL;

  BOOL is_mp_nested_pu = FALSE;

  /* read from mmap area */
  Start_Timer ( T_ReadIR_CU );
  // The current PU could already be memory as happens when the
  // compiler creates it during back end compilation of an earlier PU. 
  if (PU_Info_state (current_pu, WT_TREE) != Subsect_InMem) {
    Read_Local_Info (MEM_pu_nz_pool_ptr, current_pu);
    if (PU_Info_state (current_pu, WT_FEEDBACK) == Subsect_InMem) {
	const Pu_Hdr* pu_hdr = (const Pu_Hdr*)
	    PU_Info_feedback_ptr (current_pu);
	Cur_PU_Feedback = CXX_NEW (FEEDBACK (PU_Info_tree_ptr (current_pu),
					     MEM_pu_nz_pool_ptr,
					     pu_hdr->pu_num_inv_entries,
					     pu_hdr->pu_num_br_entries,
					     pu_hdr->pu_num_loop_entries,
					     pu_hdr->pu_num_scircuit_entries,
					     pu_hdr->pu_num_call_entries,
					     pu_hdr->pu_num_switch_entries),
				   MEM_pu_nz_pool_ptr);
	Read_Feedback_Info (Cur_PU_Feedback, PU_Info_tree_ptr (current_pu),
			    *pu_hdr);
	// turn off other feedback I/O
	Instrumentation_Enabled = FALSE;
	memset (Feedback_Enabled, '\0', PROFILE_PHASE_LAST * sizeof(BOOL));
    } else
	Cur_PU_Feedback = NULL;
  } else {			    /* retrieve transferred maps */
      // change some globals to define current_pu as the current PU
    Current_Map_Tab = PU_Info_maptab(current_pu);
    Current_pu = &PU_Info_pu(current_pu);
    CURRENT_SYMTAB = PU_lexical_level(*Current_pu);
    if ((PU_is_nested_func(*Current_pu) && PU_mp(*Current_pu)) ||
        Is_Set_PU_Info_flags(current_pu, PU_IS_DRA_CLONE)) {
      is_mp_nested_pu = TRUE;
      // hack to restore nested PU's symtab
      Restore_Local_Symtab(current_pu);

      if (PU_Info_state (current_pu, WT_FEEDBACK) == Subsect_InMem) {
              // Restore FEEDBACK object allocated by MP lowerer
	  Cur_PU_Feedback = (FEEDBACK *) PU_Info_feedback_ptr (current_pu);
	  Is_True(Cur_PU_Feedback, ("invalid PU_Info for feedback"));
              // turn off other feedback I/O
	  Instrumentation_Enabled = FALSE;
          memset(Feedback_Enabled, '\0', PROFILE_PHASE_LAST * sizeof(BOOL));
      } else
          Cur_PU_Feedback = NULL;
    } else {
      Is_True(FALSE, ("Robert doesn't understand where symtabs come from"));
    }
  }

  BE_symtab_alloc_scope_level(CURRENT_SYMTAB);
  Scope_tab[CURRENT_SYMTAB].st_tab->Register(*Be_scope_tab[CURRENT_SYMTAB].be_st_tab);

  /* NOTE: "pu" is not defined until this point, since the actual
   * (WN *) is calculated by Read_Local_Info().
   */
  pu = PU_Info_tree_ptr(current_pu);

  /* Disable all prompf processing for PUs generated by the compiler,
   * such as cloned subroutines, with exception of mp routines which
   * we do want to process (we just don't want to do the static
   * analysis part for them).  
   *
   * TODO:  Disable Anl_Static_Analysis() when this condition holds,
   * but generate the subroutines in the .m file and have the cloner
   * assign an ID map for the subroutine with unique ID numbers.
   */
  if (!Saved_run_prompf                                          &&
      Run_prompf                                                 &&
      Is_Set_PU_Info_flags(current_pu, PU_IS_COMPILER_GENERATED) &&
      !PU_mp (Get_Current_PU ())) {
    Saved_run_prompf = Run_prompf;
    Saved_run_w2c = Run_w2c;
    Saved_run_w2f = Run_w2f;
    Saved_run_w2fc_early = Run_w2fc_early;
    Run_prompf = FALSE;
    Run_w2c = FALSE;
    Run_w2f = FALSE;
    Run_w2fc_early = FALSE;
  }

  /* store original pu name */
  Orig_PU_Name = Get_Orig_PU_Name(current_pu);
  Save_Cur_PU_Name(ST_name(PU_Info_proc_sym(current_pu)), 0);

  Set_Current_PU_For_Trace(ST_name(PU_Info_proc_sym(current_pu)), 
			   Current_PU_Count());

  Stop_Timer (T_ReadIR_CU);
  Check_for_IR_Dump(TP_IR_READ,pu,"IR_READ");

  if (Show_Progress) {
    fprintf(stderr, "Compiling %s(%d)\n",
	    ST_name(PU_Info_proc_sym(current_pu)),
	    Current_PU_Count());
  }

  if (Get_Trace(TP_REGION,TT_REGION_ALL)) {
    fprintf(TFile,"===== BE driver, PU loop: PU %s(%d)\n",
	    ST_name(PU_Info_proc_sym(current_pu)),Current_PU_Count());
  }

  if (Tlog_File) {
    fprintf(Tlog_File,"BEGIN %s\n",ST_name(PU_Info_proc_sym(current_pu)));
  }

  WN_Mem_Push ();

  if (Run_wopt || Run_cg) {	    /* PU level initialization for lowerer */
    Lowering_Initialize();
  }

  if (Run_prompf && 
      !Is_Set_PU_Info_flags(current_pu, PU_IS_COMPILER_GENERATED)) {
    Prompf_Id_Map = Anl_Init_Map(MEM_pu_pool_ptr); 
    WB_ANL_Initialize(pu, Prompf_Id_Map); 
    Anl_Static_Analysis(pu, Prompf_Id_Map);
    WB_ANL_Terminate(); 
  }

  if (Run_purple) {
    Prp_Instrument_And_EmitSrc(pu);
  }

  /* Add instrumentation here for vho lower. */
  if ( Instrumentation_Enabled
       && Instrumentation_Phase_Num == PROFILE_PHASE_BEFORE_VHO ) {
    if (!is_mp_nested_pu )
      WN_Instrument(pu, PROFILE_PHASE_BEFORE_VHO); 
#if 0
    extern void wb_gwe(WN*); // hack to check __profile calls.
    wb_gwe(pu);
#endif
  } else if ( Feedback_Enabled[PROFILE_PHASE_BEFORE_VHO] ) {
    WN_Annotate(pu, PROFILE_PHASE_BEFORE_VHO, &MEM_pu_pool);
  }

  pu = VHO_Lower_Driver (current_pu, pu);

  if ( Cur_PU_Feedback ) {
    Cur_PU_Feedback->Verify("after VHO lower");
  }

  pu = Adjust_Opt_Level (current_pu, pu, ST_name(PU_Info_proc_sym(current_pu)));

  if (wopt_loaded) {
    Create_Restricted_Map(MEM_pu_nz_pool_ptr);
  }

  /* Always create region pool because there are many 
   * places where they can be introduced. Needed for PUs 
   * with no regions also */
  /* NOTE: part of what REGION_initialize does can be moved
   * to when the .B file is read in. */
  REGION_Initialize (pu, PU_has_region (Get_Current_PU ()));
  return pu;
} /* Preprocess_PU */

static void
Postprocess_PU (PU_Info *current_pu)
{
  if (Tlog_File) {
    fprintf (Tlog_File, "END %s\n", ST_name(PU_Info_proc_sym(current_pu)));
  }

  Current_Map_Tab = PU_Info_maptab(current_pu);
 
  REGION_Finalize();

  if (Run_wopt || Run_cg) {
    // delete lowering map
    Lowering_Finalize();
  }

  // Delete alias manager after CG finished ? PV 525127, 527977
  WN_Mem_Pop (); // WN pool

  if (wopt_loaded) {
    Delete_Restricted_Map();
  }

  SYMTAB_IDX scope_level = PU_lexical_level(PU_Info_pu(current_pu));

  Scope_tab[scope_level].st_tab->
    Un_register(*Be_scope_tab[scope_level].be_st_tab);
  Be_scope_tab[scope_level].be_st_tab->Clear();

  Free_Local_Info(current_pu); // deletes all maps
  MEM_POOL_Pop(MEM_pu_nz_pool_ptr);
  MEM_POOL_Pop(MEM_pu_pool_ptr);

  /* Re-enable prompf processing if relevant.  
   *
   * TODO:  Disable Anl_Static_Analysis() when this condition holds,
   * but generate the subroutines in the .m file and have the cloner
   * assign an ID map for the subroutine with unique ID numbers.
   */
  if (Saved_run_prompf) {
    Run_prompf = Saved_run_prompf;
    Run_w2c = Saved_run_w2c;
    Run_w2f = Saved_run_w2f;
    Run_w2fc_early = Saved_run_w2fc_early;
    Saved_run_prompf = FALSE;
    Saved_run_w2c = FALSE;
    Saved_run_w2f = FALSE;
    Saved_run_w2fc_early = FALSE;
  }
} /* Postprocess_PU */

/* compile each PU through all phases before going to the next PU */
static void
Preorder_Process_PUs (PU_Info *current_pu)
{
  INT orig_opt_level = Opt_Level;
  BOOL orig_run_lno = Run_lno;
  BOOL orig_run_preopt = Run_preopt;
  BOOL orig_run_wopt = Run_wopt;
  BOOL orig_olimit_opt = Olimit_opt;

  WN *pu;
  Start_Timer(T_BE_PU_CU);

  pu = Preprocess_PU(current_pu);

  // Quick! Before anyone risks creating any PREGs in the back end,
  // register the back end's PREG table with the main PREG table so
  // they will grow together as PREGs are created.
  Scope_tab[CURRENT_SYMTAB].preg_tab->Register(Be_preg_tab);

  WN_verifier(pu);

  Verify_SYMTAB (CURRENT_SYMTAB);

  if (!PU_mp (Get_Current_PU ()) &&
      (Run_Dsm_Cloner || Run_Dsm_Common_Check || Run_Dsm_Check))
    DRA_Processing(current_pu, pu, Cur_PU_Feedback != NULL);

  /* If SYMTAB_IPA_on is set then we have run ipl,
   * and therefore already done OMP_prelowering.
   * So don't do it again.
   */
  if (PU_has_mp (Get_Current_PU ()) && !FILE_INFO_ipa (File_info)) {
    Set_Error_Phase("OMP Pre-lowering");
    WB_OMP_Initialize(pu, Prompf_Id_Map);
    pu = OMP_Prelower(current_pu, pu);
    WB_OMP_Terminate(); 
  }

  if (Run_ipl) {
    Ipl_Processing (current_pu, pu);
    Verify_SYMTAB (CURRENT_SYMTAB);
  }
  else {
    Backend_Processing (current_pu, pu);
    Verify_SYMTAB (CURRENT_SYMTAB);
  }
  if (reset_opt_level) {
    Opt_Level = orig_opt_level;
    Run_lno = orig_run_lno;
    Run_preopt = orig_run_preopt;
    Run_wopt = orig_run_wopt;
    reset_opt_level = FALSE;
    Olimit_opt = orig_olimit_opt;
  }

  Scope_tab[CURRENT_SYMTAB].preg_tab->Un_register(Be_preg_tab);
  Be_preg_tab.Clear();

  Stop_Timer(T_BE_PU_CU);
  Finish_BE_Timing ( Tim_File, ST_name(PU_Info_proc_sym(current_pu)) );
  Advance_Current_PU_Count();

  Cur_PU_Name = NULL;		// memory will not be leaked; eventual
                                // pop occurs in Postprocess_PU's call
                                // to WN_MEM_Pop. Reset here is
                                // required so Save_Cur_PU_Name will
                                // not misbehave.

  // Print miscellaneous statistics to trace file:
  Print_PU_Stats ();

  // Now recursively process the child PU's.

  for (PU_Info *child = PU_Info_child(current_pu);
       child != NULL;
       child = PU_Info_next(child)) {
    Preorder_Process_PUs(child);
  }

  Postprocess_PU (current_pu);
} /* Preorder_Process_PUs */

static void Print_Tlog_Header(INT argc, char **argv)
{
  INT i;
  if (Get_Trace(TP_PTRACE1, TP_PTRACE1_NOHDR))
    return; 
  fprintf(Tlog_File,"1.0\n"); /* initial version number */
  fprintf(Tlog_File,"{ ");
  for (i=0; i<argc; i++)
    fprintf(Tlog_File,"%s ", argv[i]);
  fprintf(Tlog_File,"}\n");
}


#define FEEDBACK_PATH_MAXLEN 1024


static void
Process_Feedback_Options (OPTION_LIST* olist)
{
  if (Feedback_File_Name) {
    // this is the case where feedback files are specified implicitly.
    // We need to search the directory

    // Find last '/' in path
    INT t = strlen(Feedback_File_Name);
    Is_True(t < FEEDBACK_PATH_MAXLEN - 16,
	    ("Process_Feedback_Options: Feedback file name too long(%d)",
	     t));
    while (t > 0 && Feedback_File_Name[t - 1] != '/')
      t--;

    // Separate Feedback_File_Name into directory(path) and a file
    // name prefix.  The last character in path must be '/', so that
    // feedback file names can be appended.
    char path[FEEDBACK_PATH_MAXLEN];
    char *prefix = Feedback_File_Name + t;
    if (t > 0) {
      // Split at the '/'
      strncpy(path, Feedback_File_Name, t);
    } else {
      // No '/' in Feedback_File_Name; use "./" for current directory
      path[0] = '.';
      path[1] = '/';
      t = 2;
    }
    path[t] = '\0';
    char *dir_end = path + t;

    INT prefix_len = strlen(prefix);
    DIR* dirp = opendir(path);
    struct dirent* direntp;
    while ((direntp = readdir(dirp)) != NULL) {
      if (strncmp(direntp->d_name, prefix, prefix_len) == 0) {
	strcpy(dir_end, direntp->d_name);
	Process_Feedback_File(path);
      }
    }
    closedir(dirp);
  }

  OPTION_LIST *ol;
  for (ol = olist; ol != NULL; ol = OLIST_next(ol)) {
    char *val = OLIST_val(ol);
    Process_Feedback_File(val);
  }
} // Process_Feedback_Options


// Provide a place to stop after components are loaded
extern "C" {
  void be_debug(void) {}
}

INT
main (INT argc, char **argv)
{
  INT local_ecount, local_wcount;
  PU_Info *pu_tree;
  
  setlinebuf (stdout);
  setlinebuf (stderr);
  Handle_Signals ();
  MEM_Initialize ();
  Cur_PU_Name = NULL;
  Init_Error_Handler ( 100 );
  Set_Error_Line ( ERROR_LINE_UNKNOWN );
  Set_Error_File ( NULL );
  Set_Error_Phase ( "Back End Driver" );

  Preconfigure ();
  Process_Command_Line (argc, argv);
  if (Inhibit_EH_opt && Opt_Level > 1) Opt_Level = 1;
  Reset_Timers ();
  Start_Timer(T_BE_Comp);
  Prepare_Source ();
  Initialize_Stats ();
  Configure ();
  Configure_Source(NULL); /* Most configuration variables are set here */
#ifdef Is_True_On
  if (Get_Trace (TKIND_ALLOC, TP_MISC)) {
    MEM_Tracing_Enable();
  }
#endif
  if ( List_Enabled ) {
    Prepare_Listing_File ();
    List_Compile_Options ( Lst_File, "", FALSE, List_All_Options, FALSE );
  }

  Init_Operator_To_Opcode_Table();
    
  /* decide which phase to call */
  load_components (argc, argv);
  be_debug();

  MEM_POOL_Push (&MEM_src_pool);
  MEM_POOL_Push (&MEM_src_nz_pool);
  if ( Show_Progress ) {
    fprintf ( stderr, "Compiling %s (%s) -- Back End\n",
	     Src_File_Name, Irb_File_Name );
    fflush ( stderr );
  }
  Set_Error_Source (Src_File_Name);

  // Push initial file level options
  Options_Stack = CXX_NEW(OPTIONS_STACK(&MEM_src_nz_pool), &MEM_src_nz_pool);
  Options_Stack->Push_Current_Options();

  Start_Timer (T_ReadIR_Comp);
  if (Read_Global_Data) {
	// get input from two separate files
  	Irb_File = (FILE *)Open_Global_Input (Global_File_Name);
  	Irb_File = (FILE *)Open_Local_Input (Irb_File_Name);
  }
  else {
  	Irb_File = (FILE *)Open_Input_Info (Irb_File_Name);
  }
  Initialize_Symbol_Tables (FALSE);
  New_Scope (GLOBAL_SYMTAB, Malloc_Mem_Pool, FALSE);
  pu_tree = Read_Global_Info (NULL);
  Stop_Timer (T_ReadIR_Comp);

  Initialize_Special_Global_Symbols ();

  // if compiling an ipa-generated file, do not instrument phases that
  // have already been done at ipl time.
  if (FILE_INFO_ipa (File_info)) {
      if (Instrumentation_Enabled &&
	  Instrumentation_Phase_Num <= PROFILE_PHASE_IPA_CUTOFF) {
	  Instrumentation_Enabled = FALSE;
	  Instrumentation_Phase_Num = PROFILE_PHASE_NONE;
      }
  } else {
      Process_Feedback_Options (Feedback_Option);
  }

  //
  // Ordinarily DRA_Initialize() would run as part of Phase_Init(),
  // but we need to run it early, right here. 
  // The reason is that this code does a pre-scan to determine whether
  // any cloning might be required, and if so, sets the mp_needs_lno
  // bit in file_info. We need to know that before we do any
  // initialization and setup processing for dra/lego (e.g. loading
  // lno/wopt, doing lego_init, etc.
  //
  if (Run_Dsm_Cloner || Run_Dsm_Common_Check) {
    DRA_Initialize();
  }
  BOOL needs_lno = FILE_INFO_needs_lno (File_info);

  if (needs_lno && !Run_ipl) {
    Run_Distr_Array = TRUE;
    if (!Run_lno && !Run_autopar) {
      /* ipl is not running, and LNO has not been loaded */
      /* Has distributed arrays, so load LNO, initialize, and setup */
      INT phase_argc;
      char **phase_argv;

      if (!Run_wopt && !Run_preopt) {
	/* load wopt */
	Get_Phase_Args (PHASE_WOPT, &phase_argc, &phase_argv);
	load_so ("wopt" DSOext, WOPT_Path, Show_Progress);
	wopt_main (phase_argc, phase_argv, argc, argv);
	wopt_loaded = TRUE;
      }

      Get_Phase_Args (PHASE_LNO, &phase_argc, &phase_argv);
      load_so ("lno" DSOext, LNO_Path, Show_Progress);
      lno_main (phase_argc, phase_argv, argc, argv);
    }
  }

  /* initialize the BE symtab. Note that w2cf relies on the BE_ST */
  /* during Phase_Init and Phase_Fini                             */

  BE_symtab_initialize_be_scopes();
  BE_symtab_alloc_scope_level(GLOBAL_SYMTAB);
  SYMTAB_IDX scope_level;
  for (scope_level = 0;
       scope_level <= GLOBAL_SYMTAB;
       ++scope_level) {
    // No need to deal with levels that don't have st_tab's. Currently
    // this should be only zero.
    if (Scope_tab[scope_level].st_tab != NULL) {
      Scope_tab[scope_level].st_tab->
	Register(*Be_scope_tab[scope_level].be_st_tab);
    }
    else {
      Is_True(scope_level == 0,
	      ("Nonexistent st_tab for level %d", scope_level));
    }
  }

  Phase_Init ();

  if (Run_preopt || Run_wopt || Run_lno || Run_Distr_Array || Run_autopar 
	|| Run_cg) {
    Set_Error_Descriptor (EP_BE, EDESC_BE);
    Set_Error_Descriptor (EP_CG, EDESC_CG);
  }

  if (Tlog_File)
    Print_Tlog_Header(argc, argv);


  for (PU_Info *current_pu = pu_tree;
       current_pu != NULL;
       current_pu = PU_Info_next(current_pu)) {
    Preorder_Process_PUs(current_pu);
  }


  /* Terminate stdout line if showing PUs: */
  if (Show_Progress) {
    fprintf (stderr, "\n");
    fflush (stderr);
  }

  Phase_Fini ();

  /* free the BE symtabs. w2cf requires BE_ST in Phase_Fini */

  Is_True(scope_level == GLOBAL_SYMTAB + 1,
	  ("scope_level must be GLOBAL_SYMTAB + 1, left from earlier loop"));

  do {
    --scope_level;
    // No need to deal with levels that don't have st_tab's. Currently
    // this should be only zero.
    if (Scope_tab[scope_level].st_tab != NULL) {
      Scope_tab[scope_level].st_tab->
	Un_register(*Be_scope_tab[scope_level].be_st_tab);
      Be_scope_tab[scope_level].be_st_tab->Clear();
    }
    else {
      Is_True(scope_level == 0,
	      ("Nonexistent st_tab for level %d", scope_level));
    }
  } while (scope_level != 0);

  BE_symtab_free_be_scopes();

  
  if (need_wopt_output || need_lno_output || need_ipl_output) {
    Write_Global_Info (pu_tree);
    if (need_ipl_output)
      Ipl_Extra_Output (ir_output);
    Close_Output_Info ();
  }
  else if (Emit_Global_Data) {
    Write_Global_Info (NULL);	/* even if dummy pu, don't write any pu's */
    Close_Output_Info ();
  }

  /* Print miscellaneous statistics to trace file: */
  Print_Total_Stats ();
  if ((Opt_Level > 0 || Run_autopar) 
	&& Max_Src_Olimit > Olimit && !Olimit_opt && Show_OPT_Warnings) {
    ErrMsg (EC_File_Olimit_Exceeded, Max_Src_Olimit);
  }
    
  Stop_Timer(T_BE_Comp);
  Finish_Compilation_Timing ( Tim_File, Src_File_Name );

  MEM_POOL_Pop ( &MEM_src_pool );
  MEM_POOL_Pop ( &MEM_src_nz_pool );
#ifdef Is_True_On
	if (Get_Trace (TKIND_ALLOC, TP_MISC)) {
	    fprintf (TFile, "\n%s\tMemory allocation information after be\n", DBar);
	    MEM_Trace ();
	}
#endif

  /* If we've seen errors, note them and terminate: */
  if ( Get_Error_Count ( &local_ecount, &local_wcount ) ) {
    ecount += local_ecount;
  }
 
  if ( ecount > 0 ) {
    Terminate(Had_Internal_Error() ? RC_INTERNAL_ERROR : RC_NORECOVER_USER_ERROR) ;
  }

  /* Close and delete files as necessary: */
  Cleanup_Files ( TRUE, FALSE );

  exit ( RC_OKAY );
  /*NOTREACHED*/

} /* main */
